package momo.app.structure;

import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.StringTokenizer;
import java.util.Vector;
import java.util.concurrent.atomic.AtomicInteger;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import momo.multitree.structure.Node;

public class Peer extends Node
{
	private int commPort;
	private int statusPort;
	private AtomicInteger workload;
	private AtomicInteger pastWorkload;
	
	private Vector<InetAddress> vecIP;
	private InetAddress addressToUse;
	
	private double cost;
	private Map<String, Double> userCost;

	public Peer(String id, List<InetAddress> listIPs, InetAddress addressToUse, int commPort, int statusPort, double workloadRate, double stability, double cost)
	{
		super(id, workloadRate, stability);
		vecIP = new Vector<InetAddress>();
		this.addressToUse = addressToUse;
		this.commPort = commPort;
		this.statusPort = statusPort;
		this.cost = cost<0?0:cost;
		
		Iterator<InetAddress> iter = listIPs.iterator();
		while( iter.hasNext() )
			vecIP.add( iter.next() );
		
		workload = new AtomicInteger(0);
		pastWorkload = new AtomicInteger(1);
		
		userCost = Collections.synchronizedMap(new HashMap<String, Double>());
	}
	
	public Peer(List<InetAddress> listIPs, int commPort, int statusPort, double workloadRate, double stability, double cost)
	{
		this(listIPs.get(0).getHostAddress()+":"+statusPort+":"+new Random().nextInt(), listIPs, null, commPort, statusPort, workloadRate, stability, cost);
	}
	
	public Peer(String id, List<InetAddress> listIPs, int commPort, int statusPort, double workloadRate, double stability, double cost)
	{
		this(id, listIPs, null, commPort, statusPort, workloadRate, stability, cost);
	}
	
	public static Peer createPeer(String data)
	{
		try
		{
			StringTokenizer tokens = new StringTokenizer(data, ",");
			String id = tokens.nextToken();
			String ips = tokens.nextToken();
			int commPort = Integer.parseInt(tokens.nextToken());
			int statusPort = Integer.parseInt(tokens.nextToken());
			double workloadRate = Double.parseDouble(tokens.nextToken());
			double stability = Double.parseDouble(tokens.nextToken());
			double cost = Double.parseDouble(tokens.nextToken());
			String strUserCost = tokens.nextToken();
			
			Vector<InetAddress> vec = new Vector<InetAddress>();
			StringTokenizer ipTokens = new StringTokenizer(ips, "~");
			while ( ipTokens.hasMoreTokens() )
			{
				try
				{
					vec.add(InetAddress.getByName(ipTokens.nextToken()));
				}catch(UnknownHostException uhe)
				{
					Log log = LogFactory.getLog(Peer.class);
					if ( log.isErrorEnabled() )
						log.error("UnknownHostException", uhe);
				}
			}
			
			Peer p = new Peer(id, vec, null, commPort, statusPort, workloadRate, stability, cost);
			
			StringTokenizer userCostTokens = new StringTokenizer(strUserCost, "~");
			while ( userCostTokens.hasMoreTokens() )
			{
				StringTokenizer innerCostTokens = new StringTokenizer(userCostTokens.nextToken(), ":");
				while (innerCostTokens.hasMoreTokens())
				{
					String source = innerCostTokens.nextToken();
					double userCost = Double.parseDouble(innerCostTokens.nextToken());
					p.setUserCost(source, userCost);
				}
			}
			
			return p;
			
		}catch(Exception e)
		{
			Log log = LogFactory.getLog(Peer.class);
			if ( log.isErrorEnabled() )
				log.error("Exception", e);
		}
		return null;
	}
	
	public void incrementWorkload()
	{
		workload.incrementAndGet();
	}
	
	public void setWorkload(int workload)
	{
		this.workload.set(workload);
	}
	
	public int getWorkload()
	{
		return workload.get();
	}
	
	public void addToPastWorkload(int delta)
	{
		pastWorkload.getAndAdd(delta);
	}
	
	public int getPastWorkload()
	{
		return pastWorkload.get();
	}

	public int getCommPort() {
		return commPort;
	}

	public void setCommPort(int commPort) {
		this.commPort = commPort;
	}

	public int getStatusPort() {
		return statusPort;
	}

	public void setStatusPort(int statusPort) {
		this.statusPort = statusPort;
	}
	
	public InetAddress getAddressToUse() {
		return addressToUse;
	}

	public void setAddressToUse(InetAddress addressToUse) {
		this.addressToUse = addressToUse;
	}
	
	public void setAddressToUse(String addressToUse) throws UnknownHostException
	{
		this.addressToUse = InetAddress.getByName(addressToUse);
	}
	
	public List<InetAddress> getAvailableIP()
	{
		return Collections.unmodifiableList(vecIP);
	}
	
	public void setCost(double cost)
	{
		this.cost = cost<0?0:cost;
	}
	
	public double getCost()
	{
		return cost;
	}
	
	public void setUserCost(String source, double cost)
	{
		userCost.put(source, cost<0?0:cost);
	}
	
	public double getUserCost(String source)
	{
		Double cost = userCost.get(source);
		if ( cost == null )
			return this.cost;
		else
			return cost;
	}
	
	private String getUserCost()
	{
		StringBuffer buffer = new StringBuffer("~");
		Iterator<String> sourceIter = userCost.keySet().iterator();
		while ( sourceIter.hasNext() )
		{
			String source = sourceIter.next();
			double cost = userCost.get(source);
			buffer.append(source+":"+cost+"~");
		}
		return buffer.toString();
	}
	
	private String getAddress()
	{
		StringBuffer buffer = new StringBuffer();
		Iterator<InetAddress> iter = vecIP.iterator();
		while ( iter.hasNext() )
			buffer.append( iter.next().getHostAddress() + "~" );
		return buffer.toString();
	}
	
	public String toString()
	{
		return super.getId() + "," 
					+ getAddress() + ","
					+ commPort + ","
					+ statusPort + ","
					+ super.getWorkloadRate() + ","
					+ super.getStability() + ","
					+ cost + ","
					+ getUserCost();
	}
	
}//end of class Peer
